From: Felix Fietkau Date: Thu, 10 Jul 2025 20:34:45 +0000 (+0200) Subject: lib: add shake / fips202 X-Git-Url: http://git.openwrt.org/%22https:/collectd.org//%22/%22https:/collectd.org/%22?a=commitdiff_plain;h=0f656a5efca71b2798041240259cefdaced27883;p=project%2Funetd.git lib: add shake / fips202 Used for ML-DSA Signed-off-by: Felix Fietkau --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d2b29c..3d9dd37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ ELSE() ENDIF() ADD_DEFINITIONS(-DMLK_CONFIG_USE_NATIVE_BACKEND_ARITH) -ADD_LIBRARY(unet SHARED curve25519.c siphash.c sha512.c fprime.c f25519.c ed25519.c edsign.c auth-data.c chacha20.c pex-msg.c utils.c stun.c random.c sntrup761.c) +ADD_LIBRARY(unet SHARED curve25519.c siphash.c sha512.c fprime.c f25519.c ed25519.c edsign.c auth-data.c chacha20.c pex-msg.c utils.c stun.c random.c sntrup761.c shake.c) TARGET_LINK_LIBRARIES(unet ubox) ADD_EXECUTABLE(unetd ${SOURCES}) diff --git a/shake.c b/shake.c new file mode 100644 index 0000000..2aa1e6d --- /dev/null +++ b/shake.c @@ -0,0 +1,1010 @@ +/* + * Copyright (c) The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ +/* Based on the public domain implementation in crypto_hash/keccakc512/simple/ + * from http://bench.cr.yp.to/supercop.html by Ronny Van Keer and the public + * domain "TweetFips202" implementation from https://twitter.com/tweetfips202 by + * Gilles Van Assche, Daniel J. Bernstein, and Peter Schwabe */ + +#include +#include + +#include "shake.h" +#include "utils.h" + +#define NROUNDS 24 +#define ROL(a, offset) ((a << offset) ^ (a >> (64 - offset))) + +#define MLD_KECCAK_NROUNDS 24 +#define MLD_KECCAK_ROL(a, offset) ((a << offset) ^ (a >> (64 - offset))) + +static void +mld_keccakf1600_extract_bytes(uint64_t *state, unsigned char *data, + unsigned offset, unsigned length) +{ + unsigned i; +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t *state_ptr = (uint8_t *)state + offset; + for (i = 0; i < length; i++) + { + data[i] = state_ptr[i]; + } +#else + /* Portable version */ + for (i = 0; i < length; i++) + { + data[i] = (state[(offset + i) >> 3] >> (8 * ((offset + i) & 0x07))) & 0xFF; + } +#endif +} + +static void +mld_keccakf1600_xor_bytes(uint64_t *state, const unsigned char *data, + unsigned offset, unsigned length) +{ + unsigned i; +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t *state_ptr = (uint8_t *)state + offset; + for (i = 0; i < length; i++) + { + state_ptr[i] ^= data[i]; + } +#else + /* Portable version */ + for (i = 0; i < length; i++) + { + state[(offset + i) >> 3] ^= (uint64_t)data[i] + << (8 * ((offset + i) & 0x07)); + } +#endif +} + + +static const uint64_t mld_KeccakF_RoundConstants[MLD_KECCAK_NROUNDS] = { + (uint64_t)0x0000000000000001ULL, (uint64_t)0x0000000000008082ULL, + (uint64_t)0x800000000000808aULL, (uint64_t)0x8000000080008000ULL, + (uint64_t)0x000000000000808bULL, (uint64_t)0x0000000080000001ULL, + (uint64_t)0x8000000080008081ULL, (uint64_t)0x8000000000008009ULL, + (uint64_t)0x000000000000008aULL, (uint64_t)0x0000000000000088ULL, + (uint64_t)0x0000000080008009ULL, (uint64_t)0x000000008000000aULL, + (uint64_t)0x000000008000808bULL, (uint64_t)0x800000000000008bULL, + (uint64_t)0x8000000000008089ULL, (uint64_t)0x8000000000008003ULL, + (uint64_t)0x8000000000008002ULL, (uint64_t)0x8000000000000080ULL, + (uint64_t)0x000000000000800aULL, (uint64_t)0x800000008000000aULL, + (uint64_t)0x8000000080008081ULL, (uint64_t)0x8000000000008080ULL, + (uint64_t)0x0000000080000001ULL, (uint64_t)0x8000000080008008ULL}; + +static void +mld_keccakf1600_permute(uint64_t *state) +{ + unsigned round; + + uint64_t Aba, Abe, Abi, Abo, Abu; + uint64_t Aga, Age, Agi, Ago, Agu; + uint64_t Aka, Ake, Aki, Ako, Aku; + uint64_t Ama, Ame, Ami, Amo, Amu; + uint64_t Asa, Ase, Asi, Aso, Asu; + uint64_t BCa, BCe, BCi, BCo, BCu; + uint64_t Da, De, Di, Do, Du; + uint64_t Eba, Ebe, Ebi, Ebo, Ebu; + uint64_t Ega, Ege, Egi, Ego, Egu; + uint64_t Eka, Eke, Eki, Eko, Eku; + uint64_t Ema, Eme, Emi, Emo, Emu; + uint64_t Esa, Ese, Esi, Eso, Esu; + + /* copyFromState(A, state) */ + Aba = state[0]; + Abe = state[1]; + Abi = state[2]; + Abo = state[3]; + Abu = state[4]; + Aga = state[5]; + Age = state[6]; + Agi = state[7]; + Ago = state[8]; + Agu = state[9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for (round = 0; round < MLD_KECCAK_NROUNDS; round += 2) + { + /* prepareTheta */ + BCa = Aba ^ Aga ^ Aka ^ Ama ^ Asa; + BCe = Abe ^ Age ^ Ake ^ Ame ^ Ase; + BCi = Abi ^ Agi ^ Aki ^ Ami ^ Asi; + BCo = Abo ^ Ago ^ Ako ^ Amo ^ Aso; + BCu = Abu ^ Agu ^ Aku ^ Amu ^ Asu; + + /* thetaRhoPiChiIotaPrepareTheta(round, A, E) */ + Da = BCu ^ MLD_KECCAK_ROL(BCe, 1); + De = BCa ^ MLD_KECCAK_ROL(BCi, 1); + Di = BCe ^ MLD_KECCAK_ROL(BCo, 1); + Do = BCi ^ MLD_KECCAK_ROL(BCu, 1); + Du = BCo ^ MLD_KECCAK_ROL(BCa, 1); + + Aba ^= Da; + BCa = Aba; + Age ^= De; + BCe = MLD_KECCAK_ROL(Age, 44); + Aki ^= Di; + BCi = MLD_KECCAK_ROL(Aki, 43); + Amo ^= Do; + BCo = MLD_KECCAK_ROL(Amo, 21); + Asu ^= Du; + BCu = MLD_KECCAK_ROL(Asu, 14); + Eba = BCa ^ ((~BCe) & BCi); + Eba ^= (uint64_t)mld_KeccakF_RoundConstants[round]; + Ebe = BCe ^ ((~BCi) & BCo); + Ebi = BCi ^ ((~BCo) & BCu); + Ebo = BCo ^ ((~BCu) & BCa); + Ebu = BCu ^ ((~BCa) & BCe); + + Abo ^= Do; + BCa = MLD_KECCAK_ROL(Abo, 28); + Agu ^= Du; + BCe = MLD_KECCAK_ROL(Agu, 20); + Aka ^= Da; + BCi = MLD_KECCAK_ROL(Aka, 3); + Ame ^= De; + BCo = MLD_KECCAK_ROL(Ame, 45); + Asi ^= Di; + BCu = MLD_KECCAK_ROL(Asi, 61); + Ega = BCa ^ ((~BCe) & BCi); + Ege = BCe ^ ((~BCi) & BCo); + Egi = BCi ^ ((~BCo) & BCu); + Ego = BCo ^ ((~BCu) & BCa); + Egu = BCu ^ ((~BCa) & BCe); + + Abe ^= De; + BCa = MLD_KECCAK_ROL(Abe, 1); + Agi ^= Di; + BCe = MLD_KECCAK_ROL(Agi, 6); + Ako ^= Do; + BCi = MLD_KECCAK_ROL(Ako, 25); + Amu ^= Du; + BCo = MLD_KECCAK_ROL(Amu, 8); + Asa ^= Da; + BCu = MLD_KECCAK_ROL(Asa, 18); + Eka = BCa ^ ((~BCe) & BCi); + Eke = BCe ^ ((~BCi) & BCo); + Eki = BCi ^ ((~BCo) & BCu); + Eko = BCo ^ ((~BCu) & BCa); + Eku = BCu ^ ((~BCa) & BCe); + + Abu ^= Du; + BCa = MLD_KECCAK_ROL(Abu, 27); + Aga ^= Da; + BCe = MLD_KECCAK_ROL(Aga, 36); + Ake ^= De; + BCi = MLD_KECCAK_ROL(Ake, 10); + Ami ^= Di; + BCo = MLD_KECCAK_ROL(Ami, 15); + Aso ^= Do; + BCu = MLD_KECCAK_ROL(Aso, 56); + Ema = BCa ^ ((~BCe) & BCi); + Eme = BCe ^ ((~BCi) & BCo); + Emi = BCi ^ ((~BCo) & BCu); + Emo = BCo ^ ((~BCu) & BCa); + Emu = BCu ^ ((~BCa) & BCe); + + Abi ^= Di; + BCa = MLD_KECCAK_ROL(Abi, 62); + Ago ^= Do; + BCe = MLD_KECCAK_ROL(Ago, 55); + Aku ^= Du; + BCi = MLD_KECCAK_ROL(Aku, 39); + Ama ^= Da; + BCo = MLD_KECCAK_ROL(Ama, 41); + Ase ^= De; + BCu = MLD_KECCAK_ROL(Ase, 2); + Esa = BCa ^ ((~BCe) & BCi); + Ese = BCe ^ ((~BCi) & BCo); + Esi = BCi ^ ((~BCo) & BCu); + Eso = BCo ^ ((~BCu) & BCa); + Esu = BCu ^ ((~BCa) & BCe); + + /* prepareTheta */ + BCa = Eba ^ Ega ^ Eka ^ Ema ^ Esa; + BCe = Ebe ^ Ege ^ Eke ^ Eme ^ Ese; + BCi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi; + BCo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso; + BCu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu; + + /* thetaRhoPiChiIotaPrepareTheta(round+1, E, A) */ + Da = BCu ^ MLD_KECCAK_ROL(BCe, 1); + De = BCa ^ MLD_KECCAK_ROL(BCi, 1); + Di = BCe ^ MLD_KECCAK_ROL(BCo, 1); + Do = BCi ^ MLD_KECCAK_ROL(BCu, 1); + Du = BCo ^ MLD_KECCAK_ROL(BCa, 1); + + Eba ^= Da; + BCa = Eba; + Ege ^= De; + BCe = MLD_KECCAK_ROL(Ege, 44); + Eki ^= Di; + BCi = MLD_KECCAK_ROL(Eki, 43); + Emo ^= Do; + BCo = MLD_KECCAK_ROL(Emo, 21); + Esu ^= Du; + BCu = MLD_KECCAK_ROL(Esu, 14); + Aba = BCa ^ ((~BCe) & BCi); + Aba ^= (uint64_t)mld_KeccakF_RoundConstants[round + 1]; + Abe = BCe ^ ((~BCi) & BCo); + Abi = BCi ^ ((~BCo) & BCu); + Abo = BCo ^ ((~BCu) & BCa); + Abu = BCu ^ ((~BCa) & BCe); + + Ebo ^= Do; + BCa = MLD_KECCAK_ROL(Ebo, 28); + Egu ^= Du; + BCe = MLD_KECCAK_ROL(Egu, 20); + Eka ^= Da; + BCi = MLD_KECCAK_ROL(Eka, 3); + Eme ^= De; + BCo = MLD_KECCAK_ROL(Eme, 45); + Esi ^= Di; + BCu = MLD_KECCAK_ROL(Esi, 61); + Aga = BCa ^ ((~BCe) & BCi); + Age = BCe ^ ((~BCi) & BCo); + Agi = BCi ^ ((~BCo) & BCu); + Ago = BCo ^ ((~BCu) & BCa); + Agu = BCu ^ ((~BCa) & BCe); + + Ebe ^= De; + BCa = MLD_KECCAK_ROL(Ebe, 1); + Egi ^= Di; + BCe = MLD_KECCAK_ROL(Egi, 6); + Eko ^= Do; + BCi = MLD_KECCAK_ROL(Eko, 25); + Emu ^= Du; + BCo = MLD_KECCAK_ROL(Emu, 8); + Esa ^= Da; + BCu = MLD_KECCAK_ROL(Esa, 18); + Aka = BCa ^ ((~BCe) & BCi); + Ake = BCe ^ ((~BCi) & BCo); + Aki = BCi ^ ((~BCo) & BCu); + Ako = BCo ^ ((~BCu) & BCa); + Aku = BCu ^ ((~BCa) & BCe); + + Ebu ^= Du; + BCa = MLD_KECCAK_ROL(Ebu, 27); + Ega ^= Da; + BCe = MLD_KECCAK_ROL(Ega, 36); + Eke ^= De; + BCi = MLD_KECCAK_ROL(Eke, 10); + Emi ^= Di; + BCo = MLD_KECCAK_ROL(Emi, 15); + Eso ^= Do; + BCu = MLD_KECCAK_ROL(Eso, 56); + Ama = BCa ^ ((~BCe) & BCi); + Ame = BCe ^ ((~BCi) & BCo); + Ami = BCi ^ ((~BCo) & BCu); + Amo = BCo ^ ((~BCu) & BCa); + Amu = BCu ^ ((~BCa) & BCe); + + Ebi ^= Di; + BCa = MLD_KECCAK_ROL(Ebi, 62); + Ego ^= Do; + BCe = MLD_KECCAK_ROL(Ego, 55); + Eku ^= Du; + BCi = MLD_KECCAK_ROL(Eku, 39); + Ema ^= Da; + BCo = MLD_KECCAK_ROL(Ema, 41); + Ese ^= De; + BCu = MLD_KECCAK_ROL(Ese, 2); + Asa = BCa ^ ((~BCe) & BCi); + Ase = BCe ^ ((~BCi) & BCo); + Asi = BCi ^ ((~BCo) & BCu); + Aso = BCo ^ ((~BCu) & BCa); + Asu = BCu ^ ((~BCa) & BCe); + } + + /* copyToState(state, A) */ + state[0] = Aba; + state[1] = Abe; + state[2] = Abi; + state[3] = Abo; + state[4] = Abu; + state[5] = Aga; + state[6] = Age; + state[7] = Agi; + state[8] = Ago; + state[9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; +} + +static void +mld_keccakf1600x4_extract_bytes(uint64_t *state, unsigned char *data0, + unsigned char *data1, unsigned char *data2, + unsigned char *data3, unsigned offset, + unsigned length) +{ + mld_keccakf1600_extract_bytes(state + MLD_KECCAK_LANES * 0, data0, offset, + length); + mld_keccakf1600_extract_bytes(state + MLD_KECCAK_LANES * 1, data1, offset, + length); + mld_keccakf1600_extract_bytes(state + MLD_KECCAK_LANES * 2, data2, offset, + length); + mld_keccakf1600_extract_bytes(state + MLD_KECCAK_LANES * 3, data3, offset, + length); +} + +static void +mld_keccakf1600x4_xor_bytes(uint64_t *state, const unsigned char *data0, + const unsigned char *data1, + const unsigned char *data2, + const unsigned char *data3, unsigned offset, + unsigned length) +{ + mld_keccakf1600_xor_bytes(state + MLD_KECCAK_LANES * 0, data0, offset, + length); + mld_keccakf1600_xor_bytes(state + MLD_KECCAK_LANES * 1, data1, offset, + length); + mld_keccakf1600_xor_bytes(state + MLD_KECCAK_LANES * 2, data2, offset, + length); + mld_keccakf1600_xor_bytes(state + MLD_KECCAK_LANES * 3, data3, offset, + length); +} + +static void +mld_keccakf1600x4_permute(uint64_t *state) +{ + mld_keccakf1600_permute(state + MLD_KECCAK_LANES * 0); + mld_keccakf1600_permute(state + MLD_KECCAK_LANES * 1); + mld_keccakf1600_permute(state + MLD_KECCAK_LANES * 2); + mld_keccakf1600_permute(state + MLD_KECCAK_LANES * 3); +} + +/************************************************* + * Name: load64 + * + * Description: Load 8 bytes into uint64_t in little-endian order + * + * Arguments: - const uint8_t *x: pointer to input byte array + * + * Returns the loaded 64-bit unsigned integer + **************************************************/ +static uint64_t load64(const uint8_t x[8]) +{ + unsigned int i; + uint64_t r = 0; + + for (i = 0; i < 8; i++) + { + r |= (uint64_t)x[i] << 8 * i; + } + + return r; +} + +/************************************************* + * Name: store64 + * + * Description: Store a 64-bit integer to array of 8 bytes in little-endian + *order + * + * Arguments: - uint8_t *x: pointer to the output byte array (allocated) + * - uint64_t u: input 64-bit unsigned integer + **************************************************/ +static void store64(uint8_t x[8], uint64_t u) +{ + unsigned int i; + + for (i = 0; i < 8; i++) + { + /* Explicitly truncate to uint8_t */ + x[i] = (uint8_t)((u >> (8 * i)) & 0xFF); + } +} + +/************************************************* + * Name: keccak_init + * + * Description: Initializes the Keccak state. + * + * Arguments: - uint64_t *s: pointer to Keccak state + **************************************************/ +static void keccak_init(uint64_t s[MLD_KECCAK_LANES]) +{ + unsigned int i; + for (i = 0; i < MLD_KECCAK_LANES; i++) + { + s[i] = 0; + } +} + +/************************************************* + * Name: keccak_absorb + * + * Description: Absorb step of Keccak; incremental. + * + * Arguments: - uint64_t *s: pointer to Keccak state + * - unsigned int pos: position in current block to be absorbed + * - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) + * - const uint8_t *in: pointer to input to be absorbed into s + * - size_t inlen: length of input in bytes + * + * Returns new position pos in current block + **************************************************/ +static unsigned int keccak_absorb(uint64_t s[MLD_KECCAK_LANES], + unsigned int pos, unsigned int r, + const uint8_t *in, size_t inlen) +{ + unsigned int i; + + while (pos + inlen >= r) + { + for (i = pos; i < r; i++) + { + s[i / 8] ^= (uint64_t)*in++ << 8 * (i % 8); + } + inlen -= r - pos; + mld_keccakf1600_permute(s); + pos = 0; + } + + for (i = pos; i < pos + inlen; i++) + { + s[i / 8] ^= (uint64_t)*in++ << 8 * (i % 8); + } + + return i; +} + +/************************************************* + * Name: keccak_finalize + * + * Description: Finalize absorb step. + * + * Arguments: - uint64_t *s: pointer to Keccak state + * - unsigned int pos: position in current block to be absorbed + * - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) + * - uint8_t p: domain separation byte + **************************************************/ +static void keccak_finalize(uint64_t s[MLD_KECCAK_LANES], unsigned int pos, + unsigned int r, uint8_t p) +{ + s[pos / 8] ^= (uint64_t)p << 8 * (pos % 8); + s[r / 8 - 1] ^= 1ULL << 63; +} + +/************************************************* + * Name: keccak_squeeze + * + * Description: Squeeze step of Keccak. Squeezes arbitratrily many bytes. + * Modifies the state. Can be called multiple times to keep + * squeezing, i.e., is incremental. + * + * Arguments: - uint8_t *out: pointer to output data + * - size_t outlen: number of bytes to be squeezed (written to out) + * - uint64_t *s: pointer to input/output Keccak state + * - unsigned int pos: number of bytes in current block already + *squeezed + * - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) + * + * Returns new position pos in current block + **************************************************/ +static unsigned int keccak_squeeze(uint8_t *out, size_t outlen, + uint64_t s[MLD_KECCAK_LANES], + unsigned int pos, unsigned int r) +{ + unsigned int i; + size_t out_offset = 0; + + /* Reference: This code is re-factored from the reference implementation + * to facilitate proof with CBMC and to improve readability. + * + * Take a mutable copy of outlen to count down the number of bytes + * still to squeeze. The initial value of outlen is needed for the CBMC + * assigns() clauses. */ + size_t bytes_to_go = outlen; + + while (bytes_to_go > 0) + { + if (pos == r) + { + mld_keccakf1600_permute(s); + pos = 0; + } + for (i = pos; i < r && i < pos + bytes_to_go; i++) + { + const uint64_t lane = s[i / 8]; + out[out_offset] = (uint8_t)((lane >> (8 * (i % 8))) & 0xFF); + out_offset++; + } + bytes_to_go -= i - pos; + pos = i; + } + + return pos; +} + +/************************************************* + * Name: keccak_absorb_once + * + * Description: Absorb step of Keccak; + * non-incremental, starts by zeroeing the state. + * + * Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state + * - const unsigned int r: rate in bytes (e.g., 168 for SHAKE128) + * - const uint8_t *in: pointer to input to be absorbed into s + * - size_t inlen: length of input in bytes + * - uint8_t p: domain-separation byte for different Keccak-derived + *functions + **************************************************/ +static void keccak_absorb_once(uint64_t s[MLD_KECCAK_LANES], + const unsigned int r, const uint8_t *in, + size_t inlen, uint8_t p) +{ + unsigned int i; + + for (i = 0; i < MLD_KECCAK_LANES; i++) + { + s[i] = 0; + } + + while (inlen >= r) + { + for (i = 0; i < r / 8; i++) + { + s[i] ^= load64(in + 8 * i); + } + in += r; + inlen -= r; + mld_keccakf1600_permute(s); + } + + for (i = 0; i < inlen; i++) + { + s[i / 8] ^= (uint64_t)in[i] << 8 * (i % 8); + } + + s[i / 8] ^= (uint64_t)p << 8 * (i % 8); + s[(r - 1) / 8] ^= 1ULL << 63; +} + +/************************************************* + * Name: keccak_squeezeblocks + * + * Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each. + * Modifies the state. Can be called multiple times to keep + * squeezing, i.e., is incremental. Assumes zero bytes of current + * block have already been squeezed. + * + * Arguments: - uint8_t *out: pointer to output blocks + * - size_t nblocks: number of blocks to be squeezed (written to + *out) + * - uint64_t *s: pointer to input/output Keccak state + * - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) + **************************************************/ +static void keccak_squeezeblocks(uint8_t *out, size_t nblocks, + uint64_t s[MLD_KECCAK_LANES], unsigned int r) +{ + while (nblocks > 0) + { + mld_keccakf1600_permute(s); + mld_keccakf1600_extract_bytes(s, out, 0, r); + out += r; + nblocks -= 1; + } +} + +/************************************************* + * Name: shake128_init + * + * Description: Initilizes Keccak state for use as SHAKE128 XOF + * + * Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state + **************************************************/ +void shake128_init(keccak_state *state) +{ + keccak_init(state->s); + state->pos = 0; +} + +/************************************************* + * Name: shake128_absorb + * + * Description: Absorb step of the SHAKE128 XOF; incremental. + * + * Arguments: - keccak_state *state: pointer to (initialized) output Keccak + *state + * - const uint8_t *in: pointer to input to be absorbed into s + * - size_t inlen: length of input in bytes + **************************************************/ +void shake128_absorb(keccak_state *state, const uint8_t *in, size_t inlen) +{ + state->pos = keccak_absorb(state->s, state->pos, SHAKE128_RATE, in, inlen); +} + +/************************************************* + * Name: shake128_finalize + * + * Description: Finalize absorb step of the SHAKE128 XOF. + * + * Arguments: - keccak_state *state: pointer to Keccak state + **************************************************/ +void shake128_finalize(keccak_state *state) +{ + keccak_finalize(state->s, state->pos, SHAKE128_RATE, 0x1F); + state->pos = SHAKE128_RATE; +} + +/************************************************* + * Name: shake128_squeeze + * + * Description: Squeeze step of SHAKE128 XOF. Squeezes arbitraily many + * bytes. Can be called multiple times to keep squeezing. + * + * Arguments: - uint8_t *out: pointer to output blocks + * - size_t outlen : number of bytes to be squeezed (written to + *output) + * - keccak_state *s: pointer to input/output Keccak state + **************************************************/ +void shake128_squeeze(uint8_t *out, size_t outlen, keccak_state *state) +{ + state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE128_RATE); +} + +/************************************************* + * Name: shake128_absorb_once + * + * Description: Initialize, absorb into and finalize SHAKE128 XOF; + *non-incremental. + * + * Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak + *state + * - const uint8_t *in: pointer to input to be absorbed into s + * - size_t inlen: length of input in bytes + **************************************************/ +void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) +{ + keccak_absorb_once(state->s, SHAKE128_RATE, in, inlen, 0x1F); + state->pos = SHAKE128_RATE; +} + +/************************************************* + * Name: shake128_squeezeblocks + * + * Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of + * SHAKE128_RATE bytes each. Can be called multiple times + * to keep squeezing. Assumes new block has not yet been + * started (state->pos = SHAKE128_RATE). + * + * Arguments: - uint8_t *out: pointer to output blocks + * - size_t nblocks: number of blocks to be squeezed (written to + *output) + * - keccak_state *s: pointer to input/output Keccak state + **************************************************/ +void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) +{ + keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE); +} + +void shake128_release(keccak_state *state) +{ + (void)state; + /* TODO: add secure zeroization*/ +} + +/************************************************* + * Name: shake256_init + * + * Description: Initilizes Keccak state for use as SHAKE256 XOF + * + * Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state + **************************************************/ +void shake256_init(keccak_state *state) +{ + keccak_init(state->s); + state->pos = 0; +} + +/************************************************* + * Name: shake256_absorb + * + * Description: Absorb step of the SHAKE256 XOF; incremental. + * + * Arguments: - keccak_state *state: pointer to (initialized) output Keccak + *state + * - const uint8_t *in: pointer to input to be absorbed into s + * - size_t inlen: length of input in bytes + **************************************************/ +void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen) +{ + state->pos = keccak_absorb(state->s, state->pos, SHAKE256_RATE, in, inlen); +} + +/************************************************* + * Name: shake256_finalize + * + * Description: Finalize absorb step of the SHAKE256 XOF. + * + * Arguments: - keccak_state *state: pointer to Keccak state + **************************************************/ +void shake256_finalize(keccak_state *state) +{ + keccak_finalize(state->s, state->pos, SHAKE256_RATE, 0x1F); + state->pos = SHAKE256_RATE; +} + +/************************************************* + * Name: shake256_squeeze + * + * Description: Squeeze step of SHAKE256 XOF. Squeezes arbitraily many + * bytes. Can be called multiple times to keep squeezing. + * + * Arguments: - uint8_t *out: pointer to output blocks + * - size_t outlen : number of bytes to be squeezed (written to + *output) + * - keccak_state *s: pointer to input/output Keccak state + **************************************************/ +void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state) +{ + state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE256_RATE); +} + +/************************************************* + * Name: shake256_absorb_once + * + * Description: Initialize, absorb into and finalize SHAKE256 XOF; + *non-incremental. + * + * Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak + *state + * - const uint8_t *in: pointer to input to be absorbed into s + * - size_t inlen: length of input in bytes + **************************************************/ +void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) +{ + keccak_absorb_once(state->s, SHAKE256_RATE, in, inlen, 0x1F); + state->pos = SHAKE256_RATE; +} + +/************************************************* + * Name: shake256_squeezeblocks + * + * Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of + * SHAKE256_RATE bytes each. Can be called multiple times + * to keep squeezing. Assumes next block has not yet been + * started (state->pos = SHAKE256_RATE). + * + * Arguments: - uint8_t *out: pointer to output blocks + * - size_t nblocks: number of blocks to be squeezed (written to + *output) + * - keccak_state *s: pointer to input/output Keccak state + **************************************************/ +void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) +{ + keccak_squeezeblocks(out, nblocks, state->s, SHAKE256_RATE); +} + +void shake256_release(keccak_state *state) +{ + (void)state; + /* TODO: add secure zeroization*/ +} + +/************************************************* + * Name: shake128 + * + * Description: SHAKE128 XOF with non-incremental API + * + * Arguments: - uint8_t *out: pointer to output + * - size_t outlen: requested output length in bytes + * - const uint8_t *in: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void shake128(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) +{ + size_t nblocks; + keccak_state state; + + shake128_absorb_once(&state, in, inlen); + nblocks = outlen / SHAKE128_RATE; + shake128_squeezeblocks(out, nblocks, &state); + outlen -= nblocks * SHAKE128_RATE; + out += nblocks * SHAKE128_RATE; + shake128_squeeze(out, outlen, &state); +} + +/************************************************* + * Name: shake256 + * + * Description: SHAKE256 XOF with non-incremental API + * + * Arguments: - uint8_t *out: pointer to output + * - size_t outlen: requested output length in bytes + * - const uint8_t *in: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) +{ + size_t nblocks; + keccak_state state; + + shake256_absorb_once(&state, in, inlen); + nblocks = outlen / SHAKE256_RATE; + shake256_squeezeblocks(out, nblocks, &state); + outlen -= nblocks * SHAKE256_RATE; + out += nblocks * SHAKE256_RATE; + shake256_squeeze(out, outlen, &state); +} + +/************************************************* + * Name: sha3_256 + * + * Description: SHA3-256 with non-incremental API + * + * Arguments: - uint8_t *h: pointer to output (32 bytes) + * - const uint8_t *in: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void sha3_256(uint8_t h[SHA3_256_HASHBYTES], const uint8_t *in, size_t inlen) +{ + unsigned int i; + uint64_t s[MLD_KECCAK_LANES]; + + keccak_absorb_once(s, SHA3_256_RATE, in, inlen, 0x06); + mld_keccakf1600_permute(s); + for (i = 0; i < 4; i++) + { + store64(h + 8 * i, s[i]); + } +} + +/************************************************* + * Name: sha3_512 + * + * Description: SHA3-512 with non-incremental API + * + * Arguments: - uint8_t *h: pointer to output (64 bytes) + * - const uint8_t *in: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void sha3_512(uint8_t h[SHA3_512_HASHBYTES], const uint8_t *in, size_t inlen) +{ + unsigned int i; + uint64_t s[MLD_KECCAK_LANES]; + + keccak_absorb_once(s, SHA3_512_RATE, in, inlen, 0x06); + mld_keccakf1600_permute(s); + for (i = 0; i < 8; i++) + { + store64(h + 8 * i, s[i]); + } +} + + +static void mld_keccak_absorb_once_x4(uint64_t *s, uint32_t r, + const uint8_t *in0, const uint8_t *in1, + const uint8_t *in2, const uint8_t *in3, + size_t inlen, uint8_t p) +{ + while (inlen >= r) + { + mld_keccakf1600x4_xor_bytes(s, in0, in1, in2, in3, 0, r); + mld_keccakf1600x4_permute(s); + + in0 += r; + in1 += r; + in2 += r; + in3 += r; + inlen -= r; + } + + if (inlen > 0) + { + mld_keccakf1600x4_xor_bytes(s, in0, in1, in2, in3, 0, inlen); + } + + if (inlen == r - 1) + { + p |= 128; + mld_keccakf1600x4_xor_bytes(s, &p, &p, &p, &p, inlen, 1); + } + else + { + mld_keccakf1600x4_xor_bytes(s, &p, &p, &p, &p, inlen, 1); + p = 128; + mld_keccakf1600x4_xor_bytes(s, &p, &p, &p, &p, r - 1, 1); + } +} + +static void mld_keccak_squeezeblocks_x4(uint8_t *out0, uint8_t *out1, + uint8_t *out2, uint8_t *out3, + size_t nblocks, uint64_t *s, uint32_t r) +{ + while (nblocks > 0) + { + mld_keccakf1600x4_permute(s); + mld_keccakf1600x4_extract_bytes(s, out0, out1, out2, out3, 0, r); + + out0 += r; + out1 += r; + out2 += r; + out3 += r; + nblocks--; + } +} + +void mld_shake128x4_absorb_once(mld_shake128x4ctx *state, const uint8_t *in0, + const uint8_t *in1, const uint8_t *in2, + const uint8_t *in3, size_t inlen) +{ + memset(state, 0, sizeof(mld_shake128x4ctx)); + mld_keccak_absorb_once_x4(state->ctx, SHAKE128_RATE, in0, in1, in2, in3, + inlen, 0x1F); +} + +void mld_shake128x4_squeezeblocks(uint8_t *out0, uint8_t *out1, uint8_t *out2, + uint8_t *out3, size_t nblocks, + mld_shake128x4ctx *state) +{ + mld_keccak_squeezeblocks_x4(out0, out1, out2, out3, nblocks, state->ctx, + SHAKE128_RATE); +} + +void mld_shake128x4_init(mld_shake128x4ctx *state) { (void)state; } +void mld_shake128x4_release(mld_shake128x4ctx *state) +{ + /* Specification: Partially implements + * @[FIPS203, Section 3.3, Destruction of intermediate values] */ + (void)state; + /*mld_zeroize(state, sizeof(mld_shake128x4ctx));*/ +} + + +void mld_shake256x4_absorb_once(mld_shake256x4ctx *state, const uint8_t *in0, + const uint8_t *in1, const uint8_t *in2, + const uint8_t *in3, size_t inlen) +{ + memset(state, 0, sizeof(mld_shake256x4ctx)); + mld_keccak_absorb_once_x4(state->ctx, SHAKE256_RATE, in0, in1, in2, in3, + inlen, 0x1F); +} + +void mld_shake256x4_squeezeblocks(uint8_t *out0, uint8_t *out1, uint8_t *out2, + uint8_t *out3, size_t nblocks, + mld_shake256x4ctx *state) +{ + mld_keccak_squeezeblocks_x4(out0, out1, out2, out3, nblocks, state->ctx, + SHAKE256_RATE); +} + +void mld_shake256x4_init(mld_shake256x4ctx *state) { (void)state; } +void mld_shake256x4_release(mld_shake256x4ctx *state) +{ + /* Specification: Partially implements + * @[FIPS203, Section 3.3, Destruction of intermediate values] */ + (void)state; + /*mld_zeroize(state, sizeof(mld_shake256x4ctx));*/ +} diff --git a/shake.h b/shake.h new file mode 100644 index 0000000..36253e7 --- /dev/null +++ b/shake.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ +#ifndef MLD_FIPS202_FIPS202_H +#define MLD_FIPS202_FIPS202_H + +#include +#include + +#define SHAKE128_RATE 168 +#define SHAKE256_RATE 136 +#define SHA3_256_RATE 136 +#define SHA3_512_RATE 72 +#define MLD_KECCAK_LANES 25 +#define SHA3_256_HASHBYTES 32 +#define SHA3_512_HASHBYTES 64 + +#define MLD_KECCAK_LANES 25 +#define MLD_KECCAK_WAY 4 + +typedef struct +{ + uint64_t s[MLD_KECCAK_LANES]; + unsigned int pos; +} keccak_state; + +/* Context for non-incremental API */ +typedef struct +{ + uint64_t ctx[MLD_KECCAK_LANES * MLD_KECCAK_WAY]; +} mld_shake128x4ctx; + +typedef struct +{ + uint64_t ctx[MLD_KECCAK_LANES * MLD_KECCAK_WAY]; +} mld_shake256x4ctx; + +extern const uint64_t KeccakF_RoundConstants[]; + +void shake128_init(keccak_state *state); +void shake128_absorb(keccak_state *state, const uint8_t *in, size_t inlen); +void shake128_finalize(keccak_state *state); +void shake128_squeeze(uint8_t *out, size_t outlen, keccak_state *state); +void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); + +void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); +void shake128_release(keccak_state *state); + +void shake256_init(keccak_state *state); +void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen); +void shake256_finalize(keccak_state *state); + +void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state); +void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); +void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); +void shake256_release(keccak_state *state); + +void shake128(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); +void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen);; + +void sha3_256(uint8_t h[SHA3_256_HASHBYTES], const uint8_t *in, size_t inlen); + +void sha3_512(uint8_t h[SHA3_512_HASHBYTES], const uint8_t *in, size_t inlen); + +void mld_shake128x4_absorb_once(mld_shake128x4ctx *state, const uint8_t *in0, + const uint8_t *in1, const uint8_t *in2, + const uint8_t *in3, size_t inlen); +void mld_shake128x4_squeezeblocks(uint8_t *out0, uint8_t *out1, uint8_t *out2, + uint8_t *out3, size_t nblocks, + mld_shake128x4ctx *state); +void mld_shake128x4_init(mld_shake128x4ctx *state); +void mld_shake128x4_release(mld_shake128x4ctx *state); +void mld_shake256x4_absorb_once(mld_shake256x4ctx *state, const uint8_t *in0, + const uint8_t *in1, const uint8_t *in2, + const uint8_t *in3, size_t inlen); +void mld_shake256x4_squeezeblocks(uint8_t *out0, uint8_t *out1, uint8_t *out2, + uint8_t *out3, size_t nblocks, + mld_shake256x4ctx *state); +void mld_shake256x4_init(mld_shake256x4ctx *state); +void mld_shake256x4_release(mld_shake256x4ctx *state); + +#endif /* !MLD_FIPS202_FIPS202_H */